package com.example.rustfs.service;

import com.example.rustfs.config.RustFsConfig;
import com.example.rustfs.dao.UploadChunkDao;
import com.example.rustfs.entity.UploadChunk;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.s3.model.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.*;

/**
 * 分片上传服务
 */
@Slf4j
@Service
public class ChunkUploadService {
    
    private final UploadChunkDao uploadChunkDao;
    private final S3Client s3Client;
    private final RustFsConfig rustFsConfig;
    private final UserService userService;
    private final FileService fileService;
    
    public ChunkUploadService(UploadChunkDao uploadChunkDao, 
                             S3Client s3Client, 
                             RustFsConfig rustFsConfig,
                             UserService userService,
                             @Lazy FileService fileService) {
        this.uploadChunkDao = uploadChunkDao;
        this.s3Client = s3Client;
        this.rustFsConfig = rustFsConfig;
        this.userService = userService;
        this.fileService = fileService;
    }
    
    // 临时文件存储目录
    private static final String TEMP_DIR = System.getProperty("java.io.tmpdir") + "/rustfs-upload/";
    
    /**
     * 初始化分片上传
     */
    public Map<String, Object> initChunkUpload(Long userId, String fileName, String filePath, Long totalSize, Integer totalChunks) {
        String uploadId = UUID.randomUUID().toString();
        
        UploadChunk uploadChunk = new UploadChunk();
        uploadChunk.setUploadId(uploadId);
        uploadChunk.setUserId(userId);
        uploadChunk.setFileName(fileName);
        uploadChunk.setFilePath(filePath);
        uploadChunk.setTotalSize(totalSize);
        uploadChunk.setTotalChunks(totalChunks);
        uploadChunk.setCreateTime(LocalDateTime.now());
        uploadChunk.setUpdateTime(LocalDateTime.now());
        
        uploadChunkDao.save(uploadChunk);
        
        // 创建临时目录
        File tempDir = new File(TEMP_DIR + uploadId);
        tempDir.mkdirs();
        
        Map<String, Object> result = new HashMap<>();
        result.put("uploadId", uploadId);
        result.put("message", "分片上传初始化成功");
        
        log.info("初始化分片上传: uploadId={}, fileName={}, totalChunks={}", uploadId, fileName, totalChunks);
        return result;
    }
    
    /**
     * 上传分片
     */
    public Map<String, Object> uploadChunk(String uploadId, Integer chunkIndex, MultipartFile file) throws IOException {
        UploadChunk uploadChunk = uploadChunkDao.findByUploadId(uploadId);
        if (uploadChunk == null) {
            throw new RuntimeException("上传记录不存在");
        }
        
        // 保存分片到临时文件
        File chunkFile = new File(TEMP_DIR + uploadId + "/" + chunkIndex);
        file.transferTo(chunkFile);
        
        // 更新上传记录
        uploadChunk.getUploadedChunks().add(chunkIndex);
        uploadChunk.setUpdateTime(LocalDateTime.now());
        uploadChunkDao.save(uploadChunk);
        
        Map<String, Object> result = new HashMap<>();
        result.put("chunkIndex", chunkIndex);
        result.put("uploaded", uploadChunk.getUploadedChunks().size());
        result.put("total", uploadChunk.getTotalChunks());
        result.put("progress", uploadChunk.getProgress());
        result.put("completed", uploadChunk.isCompleted());
        
        log.debug("分片上传成功: uploadId={}, chunk={}/{}", uploadId, chunkIndex, uploadChunk.getTotalChunks());
        
        // 如果所有分片上传完成，合并文件
        if (uploadChunk.isCompleted()) {
            mergeChunks(uploadChunk);
        }
        
        return result;
    }
    
    /**
     * 合并分片
     */
    private void mergeChunks(UploadChunk uploadChunk) {
        try {
            String uploadId = uploadChunk.getUploadId();
            File tempDir = new File(TEMP_DIR + uploadId);
            File mergedFile = new File(TEMP_DIR + uploadId + "/" + uploadChunk.getFileName());
            
            // 合并所有分片
            try (FileOutputStream fos = new FileOutputStream(mergedFile)) {
                for (int i = 0; i < uploadChunk.getTotalChunks(); i++) {
                    File chunkFile = new File(TEMP_DIR + uploadId + "/" + i);
                    if (chunkFile.exists()) {
                        try (FileInputStream fis = new FileInputStream(chunkFile)) {
                            byte[] buffer = new byte[8192];
                            int len;
                            while ((len = fis.read(buffer)) != -1) {
                                fos.write(buffer, 0, len);
                            }
                        }
                        chunkFile.delete();
                    }
                }
            }
            
            // 上传到S3
            String key = uploadChunk.getFilePath();
            if (!key.isEmpty() && !key.endsWith("/")) {
                key = key + "/";
            }
            key += uploadChunk.getFileName();
            
            PutObjectRequest putRequest = PutObjectRequest.builder()
                    .bucket(rustFsConfig.getBucketName())
                    .key(key)
                    .build();
            
            s3Client.putObject(putRequest, RequestBody.fromFile(mergedFile));
            
            // 清理临时文件
            mergedFile.delete();
            tempDir.delete();
            
            // 删除上传记录
            uploadChunkDao.delete(uploadId);
            
            // 更新用户存储空间
            long storageUsed = fileService.calculateUserStorageUsed(uploadChunk.getUserId());
            userService.updateStorageUsed(uploadChunk.getUserId(), storageUsed);
            
            log.info("分片合并并上传成功: uploadId={}, key={}", uploadId, key);
        } catch (Exception e) {
            log.error("合并分片失败", e);
            throw new RuntimeException("合并分片失败: " + e.getMessage());
        }
    }
    
    /**
     * 获取上传进度
     */
    public Map<String, Object> getUploadProgress(String uploadId) {
        UploadChunk uploadChunk = uploadChunkDao.findByUploadId(uploadId);
        if (uploadChunk == null) {
            throw new RuntimeException("上传记录不存在");
        }
        
        Map<String, Object> result = new HashMap<>();
        result.put("uploadId", uploadId);
        result.put("fileName", uploadChunk.getFileName());
        result.put("uploaded", uploadChunk.getUploadedChunks().size());
        result.put("total", uploadChunk.getTotalChunks());
        result.put("progress", uploadChunk.getProgress());
        result.put("completed", uploadChunk.isCompleted());
        result.put("uploadedChunks", new ArrayList<>(uploadChunk.getUploadedChunks()));
        
        return result;
    }
    
    /**
     * 取消上传
     */
    public void cancelUpload(String uploadId) {
        UploadChunk uploadChunk = uploadChunkDao.findByUploadId(uploadId);
        if (uploadChunk != null) {
            // 清理临时文件
            File tempDir = new File(TEMP_DIR + uploadId);
            if (tempDir.exists()) {
                deleteDirectory(tempDir);
            }
            
            // 删除上传记录
            uploadChunkDao.delete(uploadId);
            
            log.info("取消上传: uploadId={}", uploadId);
        }
    }
    
    private void deleteDirectory(File directory) {
        File[] files = directory.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    file.delete();
                }
            }
        }
        directory.delete();
    }
}

